package top.stmo.reflectionTest;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 *      重点：
 *
 *      调用运行时类中指定的结构：属性、方法、构造器
 */
public class ReflectionTest6 {

    /**
     *      调用运行时类中指定的属性      --需要掌握
     *
     *    set(Object obj, Object value)     --设置运行时类中指定的属性
     *      参数一：指定设置那个对象的属性
     *      参数二：将此属性值设置为多少
     *
     *    get(Object obj)                   -- 获取运行时类中指定的属性
     *      参数一：指定获取那个对象的属性
     *
     *    开发中通常使用：getDeclaredField(String name)     --获取运行时类所有权限指定变量名的属性
     *      步骤：
     *         1.获取Class实例对象                .class / Class.forName() / 对象.getClass()
     *         2.通过Class对象获取运行时类所有权限指定变量名的属性   getDeclaredField(String name)
     *         3.通过Class对象创建运行时类的对象     newInstance()
     *         4.保证当前属性是可访问的(相当于开权限) setAccessible(boolean flag)
     *         5.使用get()/set()方法操作属性       get(Object obj) / set(Object obj, Object value)
     */
    @Test
    public void test1() throws Exception {

        /**
         *   运行时类中任意权限的属性       需要掌握
         */
        //获取Class实例对象
        Class<Student> class1 = Student.class;

        //getDeclaredField(String name)：获取运行时类所有权限指定变量名的属性
        Field name = class1.getDeclaredField("name");

        //创建运行时类的对象
        Student s1 = class1.newInstance();

        //保证当前属性是可访问的
        name.setAccessible(true);

        //get(Object obj)：设置指定对象的属性
        Object o1 = name.get(s1);
        System.out.println(o1);

        //set(Object obj, Object value)：获取指定对象的属性
        name.set(s1,"Tom");
        System.out.println(name.get(s1));

        System.out.println("************************");

        /**
         *   运行时类中public权限的属性   不需要掌握
         */
        //获取指定名的属性
        Field id = class1.getField("id");

        //创建运行时类的对象
        Student s2 = class1.newInstance();

        //获取当前运行时类指定属性的属性值
        Object o = id.get(s2);
        System.out.println(o);

        //设置当前运行时类指定属性的属性值
        id.set(s2,10);
        System.out.println(id.get(s2));
    }

    /**
     *      调用运行时类中的指定方法    --需要掌握
     *
     *      getDeclaredMethod(String name, Class<?>... parameterTypes)  --获取运行时类中指定方法
     *          参数一：指明获取方法的名称
     *          参数二：可能重载同名方法很多，指明获取方法的形参列表
     *
     *      invoke(Object obj, Object... args)      --调用方法(invoke方法的返回值即为调用方法的返回值)
     *          参数一：指明方法的调用者
     *          参数二：给方法形参赋值的实参
     *
     *      步骤：
     *         1.获取Class实例对象                .class / Class.forName() / 对象.getClass()
     *         2.通过Class对象获取运行时类所有权限指定方法名的方法
     *                         getDeclaredMethod(String name, Class<?>... parameterTypes)
     *         3.通过Class对象创建运行时类的对象                 newInstance()
     *         4.保证当前方法是可访问的(相当于开权限)             setAccessible(boolean flag)
     *         5.调用方法，invoke方法的返回值即为调用方法的返回值  invoke(Object obj,Object... args)
     *
     *         如果是静态方法，则不需要第3步通过Class对象创建运行时类的对象，而是在第5步的时候传入的
     *         第一个参数为  当前运行时类.class  (Student.class),即为通过此类调用静态方法
     */
    @Test
    public void test2() throws Exception {

        Class<Student> studentClass = Student.class;

        //创建运行时类的对象
        Student s1 = studentClass.newInstance();

        //获取指定的某个方法
        Method show = studentClass.getDeclaredMethod("show", String.class);

        //保证当前方法是可访问的
        show.setAccessible(true);

        //调用方法
        Object cn = show.invoke(s1, "CN");
        System.out.println(cn);

        System.out.println("**********如何调用静态方法**********");

        Method showDesc = studentClass.getDeclaredMethod("showDesc");

        showDesc.setAccessible(true);

        Object invoke = showDesc.invoke(Student.class);
        System.out.println(invoke);             //无返回值为null
        showDesc.invoke(null);              //同样可以调用
    }

    /**
     *      调用运行时类中的指定构造器        --不需要掌握
     *
     *      getDeclaredConstructor(Class<?>... parameterTypes)      --获取指定参数列表的构造器
     *          参数一：指明构造器的参数列表
     */
    @Test
    public void test3() throws Exception {
        Class<Student> studentClass = Student.class;
        Constructor<Student> constructor = studentClass.getDeclaredConstructor(String.class);
        //保证当前构造器是可访问的
        constructor.setAccessible(true);
        //调用构造器创建运行时类的对象
        Student tom = constructor.newInstance("Tom");
        System.out.println(tom);            //Student{name='Tom', age=0, id=0}
    }
}
